package com.car.utils;

import com.alibaba.druid.sql.visitor.functions.Char;
import com.car.config.DataSourceConfig;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * JDBC工具类
 */
public class JDBCUtils {
    // 日志
    private static final Log log = LogFactory.getLog(JDBCUtils.class);

    /**
     * 创建预编译对象
     *
     * @param sql    sql
     * @param params sql参数
     * @return 预编译对象
     */
    public static PreparedStatement createPreparedStatement(String sql, Object... params) throws Exception {
        // 获取连接对象
        Connection connection = DataSourceConfig.getConnection();
        // 获取预编译对象PreparedStatement 放入sql 比如 select * from t_user where id = ?
        PreparedStatement ps = connection.prepareStatement(sql);
        // 获取参数元数据
        ParameterMetaData parameterMetaData = ps.getParameterMetaData();
        // 获取字符串需要的参数个数 => 问号的个数
        int parameterCount = parameterMetaData.getParameterCount();
        // 问号的个数和sql参数的个数长度要一直
        if (params.length != parameterCount) {
            log.info("JDBCUtils => SQL参数不一致,sql=>" + ps + ",参数：" + Arrays.toString(params));
            throw new RuntimeException("SQL参数不一致");
        }
        // 判断什么情况下赋值
        for (int i = 1; i <= params.length; i++) {
            ps.setObject(i, params[i - 1]);
        }
        // 日志信息
        log.info("JDBCUtils => " + ps);
        return ps;
    }

    /**
     * @param sql    DML DDL 语句
     * @param params 参数
     * @throws Exception 异常
     */
    public static int update(String sql, Object... params) throws Exception {
        // 获得预编译后对象
        PreparedStatement ps = createPreparedStatement(sql, params);
        // 执行SQL
        int i = ps.executeUpdate();
        log.info("影响记录数：" + i);
        // 关闭资源
        close(ps, ps.getConnection());
        return i;
    }

    /**
     * 根据查询数据 封装成对应的实体类
     *
     * @param sql    sql
     * @param c      类型
     * @param params 参数
     * @param <T>    泛型T
     * @return 实体类
     * @throws Exception 异常
     */
    public static <T> T query(String sql, Class<T> c, Object... params) throws Exception {
        // 获得预编译后的对象
        PreparedStatement ps = createPreparedStatement(sql, params);
        // 执行SQL
        ResultSet resultSet = ps.executeQuery();
        // 获取数据库表字段 "id"	"name"	"age"	"gender"	"work_id" => workId	"salary"
        ResultSetMetaData metaData = resultSet.getMetaData();
        // 指向记录
        // 循环获取字段的值
        T res = null;
        if (resultSet.next()) {
            res = c.getConstructor().newInstance();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                // 参数： 实体类，实体类的字段名，值 底层都是使用set方法完成
                BeanUtils.setProperty(res, toConvertFiledName(metaData.getColumnName(i)), resultSet.getObject(i));
            }
        }
        // 关闭资源
        close(resultSet, ps, ps.getConnection());
        return res;
    }

    // 作业：queryList
    public static <T> List<T> queryList(String sql, Class<T> c, Object... params) throws Exception {
        // 获得预编译后的对象
        PreparedStatement ps = createPreparedStatement(sql, params);
        // 执行SQL
        ResultSet resultSet = ps.executeQuery();
        // 获取数据库表字段 "id"	"name"	"age"	"gender"	"work_id" => workId	"salary"
        ResultSetMetaData metaData = resultSet.getMetaData();
        // 创建List集合
        List<T> list = new ArrayList<>();
        // 循环遍历获取实体类对象 并且放入集合中
        while (resultSet.next()) {
            // 循环获取字段的值
            T res = c.getConstructor().newInstance();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                // 参数： 实体类，实体类的字段名，值 底层都是使用set方法完成
                BeanUtils.setProperty(res, toConvertFiledName(metaData.getColumnName(i)), resultSet.getObject(i));
            }
            list.add(res);
        }
        // 关闭资源
        close(resultSet, ps, ps.getConnection());
        return list;
    }

    @SafeVarargs
    public static <E extends AutoCloseable> void close(E... es) throws Exception {
        for (E e : es) {
            if (e != null) {
                e.close();
            }
        }
    }

    private static String toConvertFiledName(String filedName) {
        if (!filedName.contains("_")) {
            return filedName;
        }
        StringBuilder sb = new StringBuilder();
        String[] split = filedName.split("_");
        sb.append(split[0]);
        for (int i = 1; i < split.length; i++) {
            char c = (char) (split[i].charAt(0) - 32);
            sb.append(c).append(split[i].substring(1));
        }
        return sb.toString();
    }
}
